import os
import tempfile
import uuid
import zipfile
from typing import List, Optional
from fastapi import APIRouter, UploadFile, File, HTTPException, Query
from fastapi.responses import FileResponse
from pdf2image import convert_from_path, convert_from_bytes
from pdf2image.exceptions import PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError

# 创建路由器
router = APIRouter(
    prefix="/pdf2image",
    tags=["PDF转图片"],
    responses={404: {"description": "Not found"}},
)

# 默认目录
DEFAULT_INPUT_DIR = "input_files"
DEFAULT_OUTPUT_DIR = "output_files"
DEFAULT_DPI = 200

def ensure_directory_exists(directory_path):
    """确保目录存在，如果不存在则创建"""
    # 检查路径是否为空字符串或None
    if not directory_path:
        return  # 如果是空字符串或None，直接返回，不创建目录
        
    if not os.path.exists(directory_path):
        os.makedirs(directory_path)

def convert_pdf_to_images(
    pdf_path, 
    output_dir, 
    dpi=DEFAULT_DPI, 
    fmt="png", 
    first_page=None, 
    last_page=None, 
    grayscale=False,
    use_pdftocairo=True
):
    """
    将PDF文件转换为图片
    
    Args:
        pdf_path: PDF文件路径
        output_dir: 输出目录路径
        dpi: 图片DPI，默认为200
        fmt: 图片格式，默认为png
        first_page: 起始页码
        last_page: 结束页码
        grayscale: 是否转换为灰度图
        use_pdftocairo: 是否使用pdftocairo而不是pdftoppm
        
    Returns:
        生成的图片路径列表
    """
    # 确保输出目录存在
    ensure_directory_exists(output_dir)
    
    # 生成唯一的输出文件名前缀
    file_basename = os.path.basename(pdf_path)
    output_filename_prefix = f"output_{os.path.splitext(file_basename)[0]}_{uuid.uuid4().hex[:8]}"
    
    try:
        # 使用pdf2image库转换
        images = convert_from_path(
            pdf_path=pdf_path,
            dpi=dpi,
            output_folder=output_dir,
            fmt=fmt,
            first_page=first_page,
            last_page=last_page,
            grayscale=grayscale,
            use_pdftocairo=use_pdftocairo,
            output_file=output_filename_prefix,
            paths_only=True  # 返回文件路径而不是PIL Image对象，以防止内存溢出
        )
        
        return images
    except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e:
        raise ValueError(f"PDF处理错误: {str(e)}")
    except Exception as e:
        raise ValueError(f"转换PDF时出错: {str(e)}")

def convert_pdf_bytes_to_images(
    pdf_bytes, 
    output_dir, 
    dpi=DEFAULT_DPI, 
    fmt="png", 
    first_page=None, 
    last_page=None, 
    grayscale=False,
    use_pdftocairo=True
):
    """
    将PDF字节内容转换为图片
    
    Args:
        pdf_bytes: PDF字节内容
        output_dir: 输出目录路径
        dpi: 图片DPI，默认为200
        fmt: 图片格式，默认为png
        first_page: 起始页码
        last_page: 结束页码
        grayscale: 是否转换为灰度图
        use_pdftocairo: 是否使用pdftocairo而不是pdftoppm
        
    Returns:
        生成的图片路径列表
    """
    # 确保输出目录存在
    ensure_directory_exists(output_dir)
    
    # 生成唯一的输出文件名前缀
    output_filename_prefix = f"output_pdf_{uuid.uuid4().hex[:8]}"
    
    try:
        # 使用pdf2image库转换
        images = convert_from_bytes(
            pdf_file=pdf_bytes,
            dpi=dpi,
            output_folder=output_dir,
            fmt=fmt,
            first_page=first_page,
            last_page=last_page,
            grayscale=grayscale,
            use_pdftocairo=use_pdftocairo,
            output_file=output_filename_prefix,
            paths_only=True  # 返回文件路径而不是PIL Image对象，以防止内存溢出
        )
        
        return images
    except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e:
        raise ValueError(f"PDF处理错误: {str(e)}")
    except Exception as e:
        raise ValueError(f"转换PDF时出错: {str(e)}")

def create_zip_file(image_paths, output_dir, zip_filename=None):
    """
    将多个图片文件打包为一个ZIP文件
    
    Args:
        image_paths: 图片文件路径列表
        output_dir: 输出目录路径
        zip_filename: ZIP文件名，默认为自动生成
        
    Returns:
        生成的ZIP文件路径
    """
    if not zip_filename:
        zip_filename = f"pdf_images_{uuid.uuid4().hex[:8]}.zip"
    
    zip_path = os.path.join(output_dir, zip_filename)
    
    with zipfile.ZipFile(zip_path, 'w') as zipf:
        for img_path in image_paths:
            # 添加到zip文件，只使用文件名而非完整路径
            zipf.write(img_path, os.path.basename(img_path))
    
    return zip_path

# 添加路由器端点
@router.post("/convert")
async def convert_pdf_to_images_api(
    file: UploadFile = File(...),
    input_dir: Optional[str] = Query(DEFAULT_INPUT_DIR, description="输入文件目录"),
    output_dir: Optional[str] = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录"),
    dpi: Optional[str] = Query(str(DEFAULT_DPI), description="图片DPI"),
    fmt: Optional[str] = Query("png", description="图片格式 (png, jpg, tiff)"),
    first_page: Optional[str] = Query(None, description="起始页码"),
    last_page: Optional[str] = Query(None, description="结束页码"),
    grayscale: Optional[str] = Query("false", description="是否转换为灰度图"),
    use_pdftocairo: Optional[str] = Query("true", description="是否使用pdftocairo转换")
):
    """
    将PDF文件转换为图片
    
    - **file**: 要上传的PDF文件
    - **input_dir**: 输入文件保存目录，默认为'input_files'
    - **output_dir**: 输出文件保存目录，默认为'output_files'
    - **dpi**: 图片DPI，默认为200
    - **fmt**: 图片格式，可选png、jpg、tiff，默认为png
    - **first_page**: 起始页码，默认为None表示第一页
    - **last_page**: 结束页码，默认为None表示最后一页
    - **grayscale**: 是否转换为灰度图，默认为false
    - **use_pdftocairo**: 是否使用pdftocairo，通常比pdftoppm更快，默认为true
    
    注意：此接口使用pdf2image库，它是对pdftoppm和pdftocairo的Python封装，
    提供高质量的PDF到图片转换功能。支持将PDF的每一页转换为单独的图片文件。
    """
    # 检查文件类型
    if not file.filename.lower().endswith('.pdf'):
        raise HTTPException(status_code=400, detail="只接受PDF文件")
    
    # 处理默认目录
    if not input_dir or input_dir.strip() == "":
        input_dir = DEFAULT_INPUT_DIR
    
    if not output_dir or output_dir.strip() == "":
        output_dir = DEFAULT_OUTPUT_DIR
    
    # 处理默认图片格式
    if not fmt or fmt.strip() == "":
        fmt = "png"
    
    # 验证图片格式
    valid_formats = ["png", "jpg", "jpeg", "tiff"]
    if fmt.lower() not in valid_formats:
        raise HTTPException(status_code=400, detail=f"不支持的图片格式，请使用: {', '.join(valid_formats)}")
    
    # 确保目录存在
    ensure_directory_exists(input_dir)
    ensure_directory_exists(output_dir)
    
    # 处理整数参数
    dpi_value = DEFAULT_DPI
    if dpi and dpi.strip():
        try:
            dpi_value = int(dpi)
        except ValueError:
            raise HTTPException(status_code=400, detail="DPI参数必须是整数")
    
    first_page_value = None
    if first_page and first_page.strip():
        try:
            first_page_value = int(first_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="起始页码参数必须是整数")
    
    last_page_value = None
    if last_page and last_page.strip():
        try:
            last_page_value = int(last_page)
        except ValueError:
            raise HTTPException(status_code=400, detail="结束页码参数必须是整数")
    
    # 处理布尔参数
    grayscale_value = False
    if grayscale:
        grayscale_lower = grayscale.lower().strip()
        if grayscale_lower in ["true", "1", "yes", "y"]:
            grayscale_value = True
        elif grayscale_lower in ["false", "0", "no", "n", ""]:
            grayscale_value = False
        else:
            raise HTTPException(status_code=400, detail="grayscale参数必须是布尔值")
    
    use_pdftocairo_value = True
    if use_pdftocairo:
        pdftocairo_lower = use_pdftocairo.lower().strip()
        if pdftocairo_lower in ["true", "1", "yes", "y"]:
            use_pdftocairo_value = True
        elif pdftocairo_lower in ["false", "0", "no", "n", ""]:
            use_pdftocairo_value = False
        else:
            raise HTTPException(status_code=400, detail="use_pdftocairo参数必须是布尔值")
    
    # 保存上传的文件
    file_path = os.path.join(input_dir, file.filename)
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())
    
    try:
        # 处理PDF文件
        image_paths = convert_pdf_to_images(
            pdf_path=file_path,
            output_dir=output_dir,
            dpi=dpi_value,
            fmt=fmt,
            first_page=first_page_value,
            last_page=last_page_value,
            grayscale=grayscale_value,
            use_pdftocairo=use_pdftocairo_value
        )
        
        # 如果生成了多张图片，创建一个ZIP文件
        if len(image_paths) > 1:
            zip_path = create_zip_file(image_paths, output_dir)
            file_name = os.path.basename(zip_path)
            download_url = f"/outputs/{file_name}"
            
            return {
                "success": True,
                "message": f"PDF转换成功，共生成{len(image_paths)}张图片",
                "images_count": len(image_paths),
                "file_path": zip_path,
                "download_url": download_url,
                "image_paths": [os.path.basename(path) for path in image_paths]
            }
        elif len(image_paths) == 1:
            # 只有一张图片时，直接返回图片信息
            file_name = os.path.basename(image_paths[0])
            download_url = f"/outputs/{file_name}"
            
            return {
                "success": True,
                "message": "PDF转换成功，生成了1张图片",
                "images_count": 1,
                "file_path": image_paths[0],
                "download_url": download_url
            }
        else:
            raise HTTPException(status_code=500, detail="未能生成任何图片")
    except ValueError as e:
        raise HTTPException(status_code=500, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"处理文件时出错: {str(e)}")

@router.get("/download/{zip_filename}")
async def download_zip_file(
    zip_filename: str,
    output_dir: str = Query(DEFAULT_OUTPUT_DIR, description="输出文件目录")
):
    """
    下载ZIP文件
    
    - **zip_filename**: ZIP文件名
    - **output_dir**: 输出文件目录，默认为'output_files'
    """
    file_path = os.path.join(output_dir, zip_filename)
    
    if not os.path.exists(file_path):
        raise HTTPException(status_code=404, detail="文件不存在")
    
    return FileResponse(
        path=file_path, 
        filename=zip_filename, 
        media_type="application/zip"
    )
